// EG4PlaHit.cxx -- implementation of the class for hits of DCs
// Created: Jan. 23, 2019, by Asia SUN, asia.rabbit@163.com
// Last Modified: Nov. 13, 2023 by Asia SUN

#include "EG4PlaHit.h"
#include "G4VVisManager.hh"
#include "G4VisAttributes.hh"
#include "G4Circle.hh"
#include "G4Transform3D.hh"
#include "G4LogicalVolume.hh"
#include "G4AttDefStore.hh"
#include "G4AttDef.hh"
#include "G4AttValue.hh"
#include "G4UIcommand.hh"
#include "G4UnitsTable.hh"
#include "G4SystemOfUnits.hh"
#include "G4ios.hh"

using std::cout;
using std::endl;

G4ThreadLocal G4Allocator<EG4PlaHit> *EG4PlaHit::phAlloc = nullptr;

EG4PlaHit::EG4PlaHit() : G4VHit(){
  Initialize();
} // end ctor

EG4PlaHit::~EG4PlaHit(){}

void EG4PlaHit::Initialize(){
  fParentId = fTrackId = fLayerId = fStripId = -1; fType = -1; fName = "";
  fTrkLen = fCharge = fMass = fTime = fEk = fEdep = fV = -999.;
  fPos.set(0., 0., 0.); fP.set(0., 0., 0.); fRot.set(0., 0., 0.);
  fLV = nullptr;
} // end memeber function initialize

EG4PlaHit::EG4PlaHit(const EG4PlaHit &p) :  G4VHit(){
  if(&p != this) *this = p;
} // end copy ctor

const EG4PlaHit &EG4PlaHit::operator=(const EG4PlaHit &p){
  fParentId = p.fParentId; fTrackId = p.fTrackId; fType = p.fType;
  fLayerId = p.fLayerId; fStripId = p.fStripId; fName = p.fName;
  fCharge = p.fCharge; fMass = p.fMass; fTime = p.fTime;
  fEk = p.fEk; fEdep = p.fEdep; fV = p.fV; fHitPos = p.fHitPos;
  fPos = p.fPos; fP = p.fP; fRot = p.fRot; fLV = p.fLV; fTrkLen = p.fTrkLen;

  return *this;
} // end assignment ctor

bool EG4PlaHit::operator==(const EG4PlaHit &p) const{
  return fTime == p.fTime && fHitPos == p.fHitPos && fP == p.fP;
} // end member function operator==

void EG4PlaHit::Draw(){
  G4VVisManager *vm = G4VVisManager::GetConcreteInstance();
  if(!vm) return;

  G4Transform3D trans(fRot.inverse(), fPos);
  G4VisAttributes attribs;
  const G4VisAttributes *pVA = fLV->GetVisAttributes();
  if(pVA) attribs = *pVA;
  if(0 == fType) attribs.SetColour(G4Colour(1., 0., 0.)); // a primary
  if(1 == fType) attribs.SetColour(G4Colour(0., 1., 0.)); // a 2ndary charged
  if(2 == fType) attribs.SetColour(G4Colour(0., 0., 1.)); // a 2ndary neutron
  attribs.SetForceCloud(true);
  vm->Draw(*fLV, attribs, trans);

  G4Circle circle(fHitPos);
  circle.SetScreenSize(15);
  circle.SetFillStyle(G4Circle::filled);
  if(0 == fType){
    circle.SetVisAttributes(new G4VisAttributes(G4Colour(1., 0., 0.)));
    circle.SetScreenSize(30);
  }
  if(1 == fType) circle.SetVisAttributes(new G4VisAttributes(G4Colour(0., 1., 0.)));
  if(2 == fType) circle.SetVisAttributes(new G4VisAttributes(G4Colour(0., 0., 1.)));
  vm->Draw(circle);
  // Print(); // DEBUG
} // end member function Draw

const map<G4String, G4AttDef> *EG4PlaHit::GetAttDefs() const{
  bool isNew = false;
  auto store = G4AttDefStore::GetInstance("EG4PlaHit", isNew);
  if(isNew){
    (*store)["Ek"]
      //        name,   description,    category,    extra,     valueType
      = G4AttDef("Ek", "Kinetic Energy", "Physics", "G4BestUnit", "double");
    (*store)["Edep"] = G4AttDef("Edep", "Energy deposition", "Physics", "G4BestUnit", "double");
    (*store)["trkLen"] = G4AttDef("trkLen", "track length", "Physics", "G4BestUnit", "double");
    (*store)["HitType"] = G4AttDef("HitType", "Hit Type", "Physics", "", "G4String");
    (*store)["LayerId"] = G4AttDef("LayerId", "layer id", "Physics", "", "int");
    (*store)["StripId"] = G4AttDef("StripId", "strip id", "Physics", "", "int");
    (*store)["Time"] = G4AttDef("Time", "Time", "Physics", "", "double");
    (*store)["V"] = G4AttDef("V", "Velocity", "Physics", "", "double");
    (*store)["Pos"] = G4AttDef("Pos", "Position", "Physics", "", "G4ThreeVector");
    (*store)["P"] = G4AttDef("P", "Momentum", "Physics", "G4BestUnit", "G4ThreeVector");
    (*store)["LV"] = G4AttDef("LV", "Logical Volume", "Physics", "", "G4String");
  } // end if

  return store;
} // end member function GetAttDefs

vector<G4AttValue> *EG4PlaHit::CreateAttValues() const{
  vector<G4AttValue> *values = new vector<G4AttValue>;
  values->push_back(G4AttValue("HitType", "EG4Hit", ""));
  values->push_back(G4AttValue("LayerId", G4UIcommand::ConvertToString(fLayerId), ""));
  values->push_back(G4AttValue("StripId", G4UIcommand::ConvertToString(fStripId), ""));
  values->push_back(G4AttValue("Time", G4BestUnit(fTime, "Time"), ""));
  // (G4String &name, G4String &value, G4String &showLabel)
  values->push_back(G4AttValue("V", G4UIcommand::ConvertToString(fV), " mm/ns"));
  values->push_back(G4AttValue("Ek", G4BestUnit(fEk, "Energy"), ""));
  values->push_back(G4AttValue("Edep", G4BestUnit(fEdep, "Energy"), ""));
  values->push_back(G4AttValue("trkLen", G4BestUnit(fTrkLen, "Length"), ""));
  values->push_back(G4AttValue("Pos", G4BestUnit(fPos, "Length"), ""));
  values->push_back(G4AttValue("P", G4BestUnit(fP, "Momentum"), ""));
  if(fLV) values->push_back(G4AttValue("LV", fLV->GetName(), ""));
  else values->push_back(G4AttValue("LV", "", ""));

  return values;
} // end member function CreateAttValues

void EG4PlaHit::Print(){
  cout << endl << "---------- Pla " << fLV->GetName() << " HIT --------- " << endl;
  cout << "Parent ID: " << fParentId << " Track ID: " << fTrackId << endl;
  cout << (fType ? "\033[31;1m" : "\033[32;1m") << fName << "\033[0m";
  cout << " mass: " << fMass << " charge: " << fCharge << endl;
  cout << "  --" << fLV->GetName() << " ";
  cout << "Layer[" << fLayerId << "]" << " Strip[" << fStripId << "]" << endl;
  cout << "Time: " << fTime/ns << " ns" << endl;
  cout << "V: " << fV << " mm/ns, beta: " << fV / CLHEP::c_light;
  cout << ", Ek: " << G4BestUnit(fEk, "Energy");
  cout << ", Ek/u: " << G4BestUnit(fEk/fMass, "Energy") << endl;
  cout << "Edep: " << G4BestUnit(fEdep, "Energy");
  cout << ", Pos: " << fPos << ", P: " << fP << endl;
  if(-999. != fTrkLen) cout << "trkLen: " << G4BestUnit(fTrkLen, "Length") << endl;
} // end member function Print

void *EG4PlaHit::operator new(size_t){
  if(!phAlloc) phAlloc = new G4Allocator<EG4PlaHit>;
  return (void *)phAlloc->MallocSingle();
} // end overloaded new

void EG4PlaHit::operator delete(void *hit){
  phAlloc->FreeSingle((EG4PlaHit*)hit);
} // end overloaded delete

void EG4PlaHit::Assign(hit_t &t) const{
  t.pid = fParentId; t.tid = fTrackId;
  t.id[0] = fLayerId; t.id[1] = fStripId;
  t.a = fMass; t.z = fCharge; t.t = fTime;
  t.v = fV; t.ek = fEk; t.de = fEdep; t.trkLen = fTrkLen;
  EG4Data::copy(t.pos, fHitPos); EG4Data::copy(t.p, fP);
} // end member function Assign
